package gbench.common.matlib.matrix;

import gbench.common.matlib.MatlibCanvas;
import gbench.common.matlib.MatlibCanvas.NVec;
import gbench.common.matlib.MatlibCanvas.Seq;
import gbench.common.matlib.MatlibCanvas.XVec;

import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;

/**
 * 复数矩阵
 *
 * @author gbench
 */
public class ComplexMatrix extends XXVec<Complex, ComplexMatrix> {

    /**
     * 构建复数矩阵
     *
     * @param data 数据矩阵
     */
    public ComplexMatrix(final XVec<XVec<Complex>> data) {
        super(data);
    }

    /**
     *
     */
    @Override
    public ComplexMatrix duplicate(final Complex[][] dd) {
        return build(dd);
    }

    /**
     * 矩阵乘法
     *
     * @param mx 右乘矩阵
     * @return 复数矩阵
     */
    public ComplexMatrix mmult(final ComplexMatrix mx) {
        final var rows = this.rows(); // 左边行矩阵
        final var columns = mx.columns(); // 列矩阵
        final var xvec = rows.flatMap(row -> columns.map(column -> // 行、列comprehend
            row.zipS(column).map(p -> p.mutate(Complex::mul)).reduce(Complex::add).orElse(null) // 计算矩阵元素
        )).collect(XVec.xveclc());
        final var m = this.shape().i4(0); // 数据行
        final var n = mx.shape().i4(1); // 数据列
        return this.duplicate(xvec.reshape2(m, n));
    }

    /**
     * 矩阵乘法
     *
     * @param nvec 右乘矩阵
     * @return 复数矩阵
     */
    public ComplexMatrix mmult(final NVec nvec) {
        return this.mmult(V(nvec.dblA()));
    }

    /**
     * 矩阵按位加法 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param mx 另一个矩阵
     * @return 矩阵按位加法
     */
    public ComplexMatrix add(final ComplexMatrix mx) {
        return this.duplicate(this.zipX(mx, Complex::add));
    }

    /**
     * 矩阵按位加法 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param c 另一个加数
     * @return 矩阵按位加法
     */
    public ComplexMatrix add(final Complex c) {
        return this.duplicate(this.zipX(ComplexMatrix.of(c), Complex::add));
    }

    /**
     * 矩阵按位加法 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param c 另一个加数
     * @return 矩阵按位加法
     */
    public ComplexMatrix add(final Number c) {
        return this.duplicate(this.zipX(ComplexMatrix.of(Complex.C(c)), Complex::add));
    }

    /**
     * 矩阵按位减法 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param mx 减数矩阵
     * @return 矩阵按位减法
     */
    public ComplexMatrix sub(final ComplexMatrix mx) {
        return this.duplicate(this.zipX(mx, Complex::sub));
    }

    /**
     * 矩阵按位减法 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param c 减数
     * @return 矩阵按位减法
     */
    public ComplexMatrix sub(final Complex c) {
        return this.duplicate(this.zipX(ComplexMatrix.of(c), Complex::sub));
    }

    /**
     * 矩阵按位减法 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param c 减数
     * @return 矩阵按位减法
     */
    public ComplexMatrix sub(final Number c) {
        return this.duplicate(this.zipX(ComplexMatrix.of(Complex.C(c)), Complex::sub));
    }

    /**
     * 矩阵按位减法 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param mx 被减数矩阵
     * @return 矩阵按位减法
     */
    public ComplexMatrix sub2(final ComplexMatrix mx) {
        return this.duplicate(this.zipX(mx, Complex::sub2));
    }

    /**
     * 矩阵按位减法 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param c 被减数
     * @return 矩阵按位减法
     */
    public ComplexMatrix sub2(final Complex c) {
        return this.duplicate(this.zipX(ComplexMatrix.of(c), Complex::sub2));
    }

    /**
     * 矩阵按位减法 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param c 被减数
     * @return 矩阵按位减法
     */
    public ComplexMatrix sub2(final Number c) {
        return this.duplicate(this.zipX(ComplexMatrix.of(Complex.C(c)), Complex::sub2));
    }

    /**
     * 矩阵按位乘法 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param mx 另一个矩阵
     * @return 矩阵按位乘法
     */
    public ComplexMatrix mul(final ComplexMatrix mx) {
        return this.duplicate(this.zipX(mx, Complex::mul));
    }

    /**
     * 矩阵按位乘法 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param c 另一个矩阵
     * @return 矩阵按位乘法
     */
    public ComplexMatrix mul(final Complex c) {
        return this.duplicate(this.zipX(ComplexMatrix.of(c), Complex::mul));
    }

    /**
     * 矩阵按位乘法 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param c 另一个乘数
     * @return 矩阵按位乘法
     */
    public ComplexMatrix mul(final Number c) {
        return this.duplicate(this.zipX(ComplexMatrix.of(Complex.C(c)), Complex::mul));
    }

    /**
     * 矩阵按位除法 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param mx 除数矩阵
     * @return 矩阵按位除法
     */
    public ComplexMatrix div(final ComplexMatrix mx) {
        return this.duplicate(this.zipX(mx, Complex::div));
    }

    /**
     * 矩阵按位除法 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param c 除数
     * @return 矩阵按位除法
     */
    public ComplexMatrix div(final Complex c) {
        return this.duplicate(this.zipX(ComplexMatrix.of(c), Complex::div));
    }

    /**
     * 矩阵按位除法 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param c 被除数除数
     * @return 矩阵按位除法
     */
    public ComplexMatrix div(final Number c) {
        return this.div(Complex.C(c));
    }

    /**
     * 矩阵按位除法 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param mx 被除数矩阵
     * @return 矩阵按位除法
     */
    public ComplexMatrix div2(final ComplexMatrix mx) {
        return this.duplicate(this.zipX(mx, Complex::div2));
    }

    /**
     * 矩阵按位除法 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param c 被除数
     * @return 矩阵按位除法
     */
    public ComplexMatrix div2(final Complex c) {
        return this.duplicate(this.zipX(ComplexMatrix.of(c), Complex::div2));
    }

    /**
     * 矩阵按位除法 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param c 除数
     * @return 矩阵按位除法
     */
    public ComplexMatrix div2(final Number c) {
        return this.div(Complex.C(c));
    }

    /**
     * 矩阵按位乘幂 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param mx 指数矩阵
     * @return 矩阵按位乘幂
     */
    public ComplexMatrix pow(final ComplexMatrix mx) {
        return this.duplicate(this.zipX(mx, Complex::pow));
    }

    /**
     * 矩阵按位乘幂 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param c 指数
     * @return 矩阵按位乘幂
     */
    public ComplexMatrix pow(final Complex c) {
        return this.duplicate(this.zipX(ComplexMatrix.of(c), Complex::pow));
    }

    /**
     * 矩阵按位乘幂 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param c 指数
     * @return 矩阵按位乘幂
     */
    public ComplexMatrix pow(final Number c) {
        return this.pow2(Complex.C(c));
    }

    /**
     * 矩阵按位乘幂 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param mx 底数矩阵
     * @return 矩阵按位乘幂
     */
    public ComplexMatrix pow2(final ComplexMatrix mx) {
        return this.duplicate(this.zipX(mx, Complex::pow2));
    }

    /**
     * 矩阵按位乘幂 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param c 底数
     * @return 矩阵按位乘幂
     */
    public ComplexMatrix pow2(final Complex c) {
        return this.duplicate(this.zipX(ComplexMatrix.of(c), Complex::pow2));
    }

    /**
     * 矩阵按位乘幂 （this 为主矩阵，即按照this的行列进行展开,若有数据缺失采用循环补位进行补填)
     * 
     * @param c 底数
     * @return 矩阵按位乘幂
     */
    public ComplexMatrix pow2(final Number c) {
        return this.pow2(Complex.C(c));
    }

    /**
     * 求矩阵行列式
     *
     * @return 矩阵行列式
     */
    public Complex det() {
        return this.isOneXOne()
                ? this.cell(0, 0) // 1 x 1 矩阵返回第一个元素
                : this.row(0) // 按照第一行展开
                        .fmap((i, e) -> this.minorX(0, i).det().mul(i % 2 == 0 ? e : e.negate())) // 代数余子式与对应元素的乘积
                        .reduce(Complex::add).orElse(null); // 累加求和
    }

    /**
     * 两个矩阵的点积结果（一般为向量： sum(a[i,j],b[i,j])
     * @param mx 另一个矩阵
     * @return 点积结果
     */
    public Complex dot(final ComplexMatrix mx){
        return this.zipX(mx,Complex::mul).reduce2(Complex::add);
    }

    /**
     * 伴随矩阵
     *
     * @return 伴随矩阵
     */
    public ComplexMatrix adjugate() {
        // 注意这里做了矩阵转置
        return KJX(this.ncols(), this.nrows(), (k, j) -> this.minorX(j, k).det().mul(Complex.C1((k+j)%2==0?1:-1)));
    }

    /**
     * 求矩阵的逆矩阵
     * @return 逆矩阵
     */
    public ComplexMatrix inverse(){
        return this.adjugate().div(this.det());
    }

    /**
     * 换药不换瓶 映射
     * 
     * @param mapper 数据映射
     * @return 变换后的数据结果
     */
    public ComplexMatrix fmap(final Function<Complex, Complex> mapper) {
        return this.fmap(mapper, ComplexMatrix::new);
    }

    /**
     * 生成复数矩阵(行顺序填满)
     * 
     * @param m         行数
     * @param n         列数
     * @param generator 矩阵元素生成器,(i:行号索引从0开始,j:列号索引从0开始)->c
     * @return 复数矩阵
     */
    public static ComplexMatrix of(final int m, final int n, final BiFunction<Integer, Integer, Complex> generator) {
        return XXVec.OF(m, n, generator).mutateX(ComplexMatrix::new);
    }

    /**
     * 生成复数矩阵(行顺序填满) of 的别名, 其中K行索引,K列索引,X矩阵元素数据
     * 
     * @param m         行数
     * @param n         列数
     * @param generator 矩阵元素生成器,(k:行号索引从0开始,j:列号索引从0开始)->c
     * @return 复数矩阵
     */
    public static ComplexMatrix KJX(final int m, final int n, final BiFunction<Integer, Integer, Complex> generator) {
        return XXVec.MXN(m, n, generator).mutateX(ComplexMatrix::new);
    }

    /**
     * 生成复数矩阵(行顺序填满)
     * 
     * @param m  行数
     * @param n  列数
     * @param tt 矩阵元素,tt 数据会循环补填 以填满 m x n 矩阵
     * @return 复数矩阵
     */
    public static ComplexMatrix of(final int m, final int n, final Complex... tt) {
        return ComplexMatrix.build(XXVec.OF(m, n, tt).fmap(Seq::toArray).toArray());
    }

    /**
     * 生成复数矩阵(行顺序填满)
     * 
     * @param m  行数
     * @param n  列数
     * @param tt 矩阵元素,tt 数据会循环补填 以填满 m x n 矩阵
     * @return 复数矩阵
     */
    public static ComplexMatrix of(final int m, final int n, final Number... tt) {
        return ComplexMatrix.of(m, n, Complex.ofX(tt));
    }

    /**
     * 生成复数矩阵(行顺序填满)
     * 
     * @param m  行数
     * @param n  列数
     * @param tt 矩阵元素,tt 数据会循环补填 以填满 m x n 矩阵
     * @return 复数矩阵
     */
    public static ComplexMatrix of(final int m, final int n, XVec<Complex> tt) {
        return ComplexMatrix.of(m, n, tt.toArray());
    }

    /**
     * 把单个元素视为一个矩阵
     * 
     * @param c 复数元素
     * @return c 复数元素
     */
    public static ComplexMatrix of(final Complex c) {
        return ComplexMatrix.of(1, 1, c);
    }

    /**
     * 复数矩阵
     *
     * @param data 复数矩阵
     * @return ComplexMatrix
     */
    public static ComplexMatrix build(final Complex[][] data) {
        return new ComplexMatrix(OF(data));
    }

    /**
     * 复数向量
     *
     * @param data 向量数据
     * @return ComplexMatrix
     */
    public static ComplexMatrix V(final NVec data) {

        return V(data.dblA());
    }

    /**
     * 复数向量
     *
     * @param data 向量数据
     * @return ComplexMatrix
     */
    public static ComplexMatrix V(Number... data) {

        return V(Complex.ofX(data));
    }

    /**
     * 复数向量
     *
     * @param data 矩阵数据
     * @return ComplexMatrix
     */
    public static ComplexMatrix V(Complex... data) {

        return new ComplexMatrix(XVec.OF(XVec.OF(data))).transpose();
    }

    /**
     * 复数向量
     *
     * @param rng 矩阵数据
     * @return ComplexMatrix
     */
    public static ComplexMatrix V(final MatlibCanvas.Range rng) {

        return V(rng.map(Complex::C1));
    }

    /**
     * 复数向量
     *
     * @param data 矩阵数据
     * @return ComplexMatrix
     */
    public static ComplexMatrix V(Stream<Complex> data) {

        return new ComplexMatrix(XVec.OF(XVec.OF(data))).transpose();
    }

    /**
     * 复数向量
     *
     * @param data 向量数据
     * @return ComplexMatrix
     */
    public static ComplexMatrix V(XVec<Complex> data) {

        return V(data.toArray());
    }

}